package de.develop.server;

import de.develop.enums.ClientStates;
import java.net.*;
import java.io.*;

import de.develop.commands.Commands;
import de.develop.enums.DebugTypes;
import java.util.LinkedList;
import java.util.Queue;

/**
 * The ClientConnection handles individual client connections to the game server.
 *
 */
public class Player extends Thread
{

    protected Socket socket;
    protected BufferedReader socketIn;
    protected PrintWriter socketOut;
    protected GameServer gameServer;
    protected boolean isReady;
    protected boolean isPlaying;
    protected int playerId;
    protected ClientStates clientState = ClientStates.UNDEFINED;
    private Queue<String> packets = new LinkedList<String>();
    private Player opponent;


    /*
     * Creates a new instance of ChatServerConnection.
     *
     * @param socket the client's socket connection
     * @param server the server to each the client is connected
     **/
    public Player( Socket socket, GameServer server )
    {
        this.socket = socket;
        this.gameServer = server;
        isReady = false;
        isPlaying = false;
        playerId = -1;
        clientState = ClientStates.CONNECTED;
    }

    public void setPlayerId( int playerId )
    {
        this.playerId = playerId;
    }

    /**
     * Gets the remote address of the client.
     *
     * @return the socket address of the client connection
     */
    public SocketAddress getRemoteAddress()
    {
        return socket.getRemoteSocketAddress();
    }

    /**
     * Roots a debug message to the main application.
     * 
     * @param msg The debug message to be sent to the main application
     */
    protected void debug( String msg )
    {
        Main.debug( DebugTypes.MAIN,"ClientConnection (" + socket.getRemoteSocketAddress() + ")", msg );
    }

    /**
     * Waits from messages from the client and then instructs the server to send the messages to all clients.
     */
    @Override
    public void run()
    {
        try
        {
            socketIn = new BufferedReader( new InputStreamReader( socket.getInputStream() ) );
            socketOut = new PrintWriter( socket.getOutputStream(), true );
            
            //gameServer.writeToAll( this.getRemoteAddress() + " has connected." );
            String line = this.socketIn.readLine();

            while ( line != null )
            {
                debug( "client says '" + line + "'" );
                if ( !isPlaying )
                {
                    processPacket( line );
                    // If it's a quit command, we remove the client from the server and exit
                    if ( line.compareToIgnoreCase( "bye" ) == 0 )
                    {
                        if ( gameServer.remove( this.getRemoteAddress() ) )
                        {
                            this.finalize();

                            return;
                        }
                    }

                }
                else
                {
                    if ( opponent != null )
                    {
                        opponent.write( line + "SETPOS" );
                    }

                    //debug("queue size: " + packets.size());
                }

                // this.server.writeToAll(this.getRemoteAddress() + ": " + line);
                line = socketIn.readLine();
            }
        }
        catch ( IOException e )
        {

            debug( "Exception (ClientConnectionHandler): " + e.getMessage() );
        }
    }

    public String processPacket( String line )
    {

        if ( line.compareToIgnoreCase( Commands.HAI ) == 0 )
        {
            clientState = ClientStates.VALID_CONNECTED;
            write( Commands.SERVER_RESPONSE_OK );
        }
        if ( line.compareToIgnoreCase( Commands.READY_TO_PLAY ) == 0 )
        {
            isReady = true;
            write( Commands.SERVER_RESPONSE_READY );
        }

        return null;
    }

    /**
     * Sends a message to the connected party.
     *
     * @param msg the message to send
     */
    public void write( String msg )
    {
        try
        {
            this.socketOut.write( msg + "\u0000" );
            this.socketOut.flush();
        }
        catch ( Exception e )
        {
            debug( "Exception (write): " + e.getMessage() );
        }
    }

    /**
     * Closes the reader, the writer and the socket.
     */
    protected void finalize()
    {
        try
        {
            this.socketIn.close();
            this.socketOut.close();
            this.socket.close();
            debug( "connection closed" );
        }
        catch ( Exception e )
        {
            debug( "Exception (finalize): " + e.getMessage() );
        }
    }

    public synchronized Queue<String> getPackets()
    {
        return packets;
    }

    public synchronized void setPackets( Queue<String> packets )
    {
        this.packets = packets;
    }

    public boolean isIsPlaying()
    {
        return isPlaying;
    }

    public void setIsPlaying( boolean isPlaying )
    {
        this.isPlaying = isPlaying;
    }

    public Player getOpponent()
    {
        return opponent;
    }

    public void setOpponent( Player opponent )
    {
        this.opponent = opponent;
    }
}